#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/time.h>
#include <string.h>
#include <fcntl.h>
#include <linux/fb.h>
#include <sys/ioctl.h>
#include <time.h>
#include <pthread.h>
#include "font.h"

//API
#include <errno.h>
#include <strings.h>
#include <signal.h>
#include <net/if.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include "cJSON/cJSON.h"
#include <linux/input.h>

int a=0;

//API
char buf1_city[100]={0};
char buf2[100]={0};
char buf3[100]={0};
char buf4[100]={0};
char buffer[100];//温湿度

int ts_x; //保存触摸屏x坐标
int ts_y; //保存触摸屏y坐标
int tsfd; //保存触摸屏的文件描述符
int data=1;//数据的实时与停止，1实时，0停止

void showbitmap(bitmap *bm, char *p, int x, int y);
int show_anybmp(int w,int h,int x,int y,const char *bmpname);
int size(int lcd);

// 读取一行（以 \r\n 结尾）
char *read_line(int sockfd) {
    char *line = calloc(1, 1024);
    int n = 0;
    while (1) {
        read(sockfd, line + n, 1);
        n++;
        if (n >= 2 && line[n - 2] == '\r' && line[n - 1] == '\n') {
            line[n - 2] = '\0'; // 去掉 \r\n
            break;
        }
    }
    return line;
}

// 读取分块传输编码的响应体
char *read_chunked_body(int sockfd) {
    char *body = calloc(1, 4096);
    int body_length = 0;

    while (1) {
        // 读取块大小
        char *chunk_size_line = read_line(sockfd);
        int chunk_size = strtol(chunk_size_line, NULL, 16); // 十六进制转整数
        free(chunk_size_line);

        if (chunk_size == 0) {
            // 读取最后一个块的 \r\n
            read_line(sockfd);
            break;
        }

        // 读取块数据
        char *chunk_data = calloc(1, chunk_size + 2); // 额外空间用于 \r\n
        read(sockfd, chunk_data, chunk_size);
        chunk_data[chunk_size] = '\0';

        // 将块数据追加到 body
        memcpy(body + body_length, chunk_data, chunk_size);
        body_length += chunk_size;

        // 读取块数据后的 \r\n
        read_line(sockfd);
        free(chunk_data);
    }

    return body;
}

void *content1(void *arg)
{
    font *f = fontLoad("simfang.ttf"); // 指定字库文件，比如simfang.ttf
    
    int lcd=open("/dev/fb0",O_RDWR);

    // 申请一块映射内存
    char *p=calloc(1,size(lcd));
    p = mmap(NULL, size(lcd),PROT_READ|PROT_WRITE,MAP_SHARED, lcd, 0); 
    bzero(p,size(lcd));

    //3.设置字体输出框的大小
    bitmap *bm;       
    bm=createBitmapWithInit(800,60,4,0x000000ff);
    int n=0;
    while(1)
    {
        fontSetSize(f, 50);
        fontPrint(f,bm,800-n%1700,0,"温馨提示：非必要不出门，出门戴口罩！",0x0000ff00,900);     
        showbitmap(bm,p, 0, 420);
        bzero(bm->map,bm->height*bm->width*bm->byteperpixel);        
        n++;
        
        if(n%1700==0)
        {
            a=1;
        }
        usleep(100);
    }
}

void *ui(void *arg)
{
    while(1)
    {
        // show_anybmp(60,40,600,330,"./01.bmp");
        // show_anybmp(60,40,680,330,"./02.bmp");

        font *f = fontLoad("simfang.ttf"); // 指定字库文件，比如simfang.ttf
    
        int lcd=open("/dev/fb0",O_RDWR);
    
        // 申请一块映射内存
        char *p=calloc(1,size(lcd));
        p = mmap(NULL, size(lcd),PROT_READ|PROT_WRITE,MAP_SHARED, lcd, 0); 
        bzero(p,size(lcd));
    
        //2.设置字体的大小
        fontSetSize(f, 35);
        //3.设置字体输出框的大小
        bitmap *bm;       
        bm=createBitmapWithInit(300,50,4,0xffffffff);
    
        struct timeval tv;
        time_t time;
        struct tm *p_time;
        
        while(1)
        {   
            //4.把字体输出到输出框中   
            fontPrint(f, bm, 10, 5,"[实时]    [停止]", 0x0000ff00, 800);
    
            //5.将bm妥善地放置到LCD上显示出来
            showbitmap(bm,p, 0, 370);
    
            bzero(bm->map,bm->height*bm->width*bm->byteperpixel);
            usleep(1000);
        }
    }
    
}

void *days(void *arg)
{
    font *f = fontLoad("simfang.ttf"); // 指定字库文件，比如simfang.ttf
    
    int lcd=open("/dev/fb0",O_RDWR);

    // 申请一块映射内存
    char *p=calloc(1,size(lcd));
    p = mmap(NULL, size(lcd),PROT_READ|PROT_WRITE,MAP_SHARED, lcd, 0); 
    bzero(p,size(lcd));

    //2.设置字体的大小
    fontSetSize(f, 40);
    //3.设置字体输出框的大小
    bitmap *bm;       
    bm=createBitmapWithInit(800,50,4,0xffffffff);

    struct timeval tv;
    time_t time;
    struct tm *p_time;
    
    char a[7][4]={"天","一","二","三","四","五","六"};
    int year;
    while(1)
    {   
        gettimeofday(&tv,NULL);//获取系统时间，并保存到tv中，注意这里可以精确到微秒
        // time=tv.tv_sec+208003785;
        time=tv.tv_sec;
        p_time=localtime(&time);//将系统时间转换为本地时间，考虑到本地时区和夏令时标志
        year=p_time->tm_year+1900;
        char buf[100]={0};
        sprintf(buf,"%d年%d月%d日 星期%s %d点%d分%2d秒",year,p_time->tm_mon+1,p_time->tm_mday,a[p_time->tm_wday],p_time->tm_hour,p_time->tm_min,p_time->tm_sec);
       
        //4.把字体输出到输出框中   
        fontPrint(f, bm, 10, 5,buf, 0x00ff0000, 800);

        //5.将bm妥善地放置到LCD上显示出来
        showbitmap(bm,p, 0, 0);

        bzero(bm->map,bm->height*bm->width*bm->byteperpixel);
        usleep(1000);
    }
}

void *api_info_1(void *arg)
{
    font *f = fontLoad("simfang.ttf"); // 指定字库文件，比如simfang.ttf
    
    int lcd=open("/dev/fb0",O_RDWR);

    // 申请一块映射内存
    char *p=calloc(1,size(lcd));
    p = mmap(NULL, size(lcd),PROT_READ|PROT_WRITE,MAP_SHARED, lcd, 0); 
    bzero(p,size(lcd));

    //2.设置字体的大小
    fontSetSize(f, 35);
    //3.设置字体输出框的大小
    bitmap *bm;       
    bm=createBitmapWithInit(800,50,4,0xffffffff);

    struct timeval tv;
    time_t time;
    struct tm *p_time;
    
    while(1)
    {   
        //4.把字体输出到输出框中   
        fontPrint(f, bm, 10, 5,buf1_city, 0x00ff0000, 800);

        //5.将bm妥善地放置到LCD上显示出来
        showbitmap(bm,p, 0, 60);

        bzero(bm->map,bm->height*bm->width*bm->byteperpixel);
        usleep(1000);
    }
}

void *api_info_2(void *arg)
{
    font *f = fontLoad("simfang.ttf"); // 指定字库文件，比如simfang.ttf
    
    int lcd=open("/dev/fb0",O_RDWR);

    // 申请一块映射内存
    char *p=calloc(1,size(lcd));
    p = mmap(NULL, size(lcd),PROT_READ|PROT_WRITE,MAP_SHARED, lcd, 0); 
    bzero(p,size(lcd));

    //2.设置字体的大小
    fontSetSize(f, 35);
    //3.设置字体输出框的大小
    bitmap *bm;       
    bm=createBitmapWithInit(800,50,4,0xffffffff);

    struct timeval tv;
    time_t time;
    struct tm *p_time;
    
    while(1)
    {   
        //4.把字体输出到输出框中   
        fontPrint(f, bm, 10, 5,buf2, 0x00ff0000, 800);

        //5.将bm妥善地放置到LCD上显示出来
        showbitmap(bm,p, 0, 120);

        bzero(bm->map,bm->height*bm->width*bm->byteperpixel);
        usleep(1000);
    }
}

void *api_info_3(void *arg)
{
    font *f = fontLoad("simfang.ttf"); // 指定字库文件，比如simfang.ttf
    
    int lcd=open("/dev/fb0",O_RDWR);

    // 申请一块映射内存
    char *p=calloc(1,size(lcd));
    p = mmap(NULL, size(lcd),PROT_READ|PROT_WRITE,MAP_SHARED, lcd, 0); 
    bzero(p,size(lcd));

    //2.设置字体的大小
    fontSetSize(f, 35);
    //3.设置字体输出框的大小
    bitmap *bm;       
    bm=createBitmapWithInit(800,50,4,0xffffffff);

    struct timeval tv;
    time_t time;
    struct tm *p_time;
    
    while(1)
    {   
        //4.把字体输出到输出框中   
        fontPrint(f, bm, 10, 5,buf3, 0x00ff0000, 800);

        //5.将bm妥善地放置到LCD上显示出来
        showbitmap(bm,p, 0, 180);

        bzero(bm->map,bm->height*bm->width*bm->byteperpixel);
        usleep(1000);
    }
}

void *api_info_4(void *arg)
{
    font *f = fontLoad("simfang.ttf"); // 指定字库文件，比如simfang.ttf
    
    int lcd=open("/dev/fb0",O_RDWR);

    // 申请一块映射内存
    char *p=calloc(1,size(lcd));
    p = mmap(NULL, size(lcd),PROT_READ|PROT_WRITE,MAP_SHARED, lcd, 0); 
    bzero(p,size(lcd));

    //2.设置字体的大小
    fontSetSize(f, 35);
    //3.设置字体输出框的大小
    bitmap *bm;       
    bm=createBitmapWithInit(800,50,4,0xffffffff);

    struct timeval tv;
    time_t time;
    struct tm *p_time;
    
    while(1)
    {   
        //4.把字体输出到输出框中   
        fontPrint(f, bm, 10, 5,buf4, 0x00ff0000, 800);

        //5.将bm妥善地放置到LCD上显示出来
        showbitmap(bm,p, 0, 240);

        bzero(bm->map,bm->height*bm->width*bm->byteperpixel);
        usleep(1000);
    }
}

void *ecs_info(void *arg)
{
    font *f = fontLoad("simfang.ttf"); // 指定字库文件，比如simfang.ttf
    
    int lcd=open("/dev/fb0",O_RDWR);

    // 申请一块映射内存
    char *p=calloc(1,size(lcd));
    p = mmap(NULL, size(lcd),PROT_READ|PROT_WRITE,MAP_SHARED, lcd, 0); 
    bzero(p,size(lcd));

    //2.设置字体的大小
    fontSetSize(f, 35);
    //3.设置字体输出框的大小
    bitmap *bm;       
    bm=createBitmapWithInit(800,50,4,0xffffffff);

    struct timeval tv;
    time_t time;
    struct tm *p_time;
    
    while(1)
    {   
        while(data)
        {
        //4.把字体输出到输出框中   
        fontPrint(f, bm, 10, 5,buffer, 0x00ff0000, 800);

        //5.将bm妥善地放置到LCD上显示出来
        showbitmap(bm,p, 0, 300);

        bzero(bm->map,bm->height*bm->width*bm->byteperpixel);
        usleep(1000);
        }
        usleep(100);
    }
}

//线程获取坐标
void *readts(void *argv) 
{
	//打开触摸屏的驱动
	tsfd=open("/dev/input/event0",O_RDWR);
	if(tsfd==-1)
	{
		printf("打开触摸屏失败!\n");
	}

    struct input_event myevent;
    while(1) 
    {
        // 读取触摸屏事件
        read(tsfd, &myevent, sizeof(struct input_event));

        // 处理坐标事件
        if (myevent.type == EV_ABS)  // 触摸事件
        {
            if (myevent.code == ABS_X)  // X 坐标
            {
                ts_x = (800 * myevent.value) / 1024;
                printf("X: %d\n", ts_x);
            }
            else if (myevent.code == ABS_Y)  // Y 坐标
            {
                ts_y = (480 * myevent.value) / 600;
                printf("Y: %d\n", ts_y);
            }
        }

        if(ts_x>0&&ts_x<120&&ts_y>370&&ts_y<410)//实时
		{
			ts_x=0;
			ts_y=0;
            data = 1;
		}
        if(ts_x>180&&ts_x<300&&ts_y>370&&ts_y<410)//停止
		{
			ts_x=0;
			ts_y=0;
            data = 0;
		}
    }
    return NULL;  // 线程退出（实际不会执行到这里）
}

void api()
{
    // 获取指定服务器 IP
    struct hostent *p;
    p = gethostbyname("getweather.market.alicloudapi.com");
    if (p == NULL) {
        printf("无法解析服务器地址\n");
    }

    // 创建 TCP 连接
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0) {
        printf("创建套接字失败\n");
    }

    struct sockaddr_in addr;
    socklen_t len = sizeof(addr);
    bzero(&addr, len);
    addr.sin_family = AF_INET;
    addr.sin_addr = *(struct in_addr *)((p->h_addr_list)[0]);
    addr.sin_port = htons(80);

    if (connect(sockfd, (struct sockaddr *)&addr, len) != 0) {
        printf("连接失败\n");
    }

    // 发送 HTTP 请求
    char *http_request();
    char *s = http_request();
    write(sockfd, s, strlen(s));

    // 接收 HTTP 响应头部
    char *http_response = calloc(1, 1024);
    if (http_response == NULL) {
        printf("内存分配失败\n");
    }

    int n = 0;
    while (1) {
        read(sockfd, http_response + n, 1);
        n++;

        if (strstr(http_response, "\r\n\r\n") || (n >= 1024)) {
            break;
        }
    }
    // printf("HTTP响应头部:\n%s", http_response);

    // 读取分块传输编码的响应体
    char *json = read_chunked_body(sockfd);
    // printf("HTTP响应报体:(JSON)[%lu字节]\n%s\n", strlen(json), json);

    // 解析 cJSON
    cJSON *root = cJSON_Parse(json);
    if (root == NULL) {
        printf("JSON 解析失败: %s\n", cJSON_GetErrorPtr());
    }

    // 获取 data 对象
    cJSON *data = cJSON_GetObjectItem(root, "data");
    if (data == NULL) {
        printf("data 解析失败\n");
        cJSON_Delete(root);
    }

    // 解析城市信息
    cJSON *cityInfo = cJSON_GetObjectItem(data, "cityInfo");
    if (cityInfo != NULL) {
        sprintf(buf1_city, "%s %s %s 城市ID:%s\n",
                cJSON_GetObjectItem(cityInfo, "areaCn")->valuestring,
                cJSON_GetObjectItem(cityInfo, "cityCn")->valuestring,
                cJSON_GetObjectItem(cityInfo, "provCn")->valuestring,
                cJSON_GetObjectItem(cityInfo, "areaId")->valuestring);
        printf("%s", buf1_city);
    }

    // 解析白天天气信息
    cJSON *day = cJSON_GetObjectItem(data, "day");
    if (day != NULL) {
        sprintf(buf2, "%s %s 温度:%s %s %s\n",
                cJSON_GetObjectItem(day, "sunUp")->valuestring,
                cJSON_GetObjectItem(day, "weather")->valuestring,
                cJSON_GetObjectItem(day, "temperature")->valuestring,
                cJSON_GetObjectItem(day, "wind")->valuestring,
                cJSON_GetObjectItem(day, "wind_pow")->valuestring);
        printf("%s", buf2);
    }

    // 解析夜间天气信息
    cJSON *night = cJSON_GetObjectItem(data, "night");
    if (night != NULL) {
        sprintf(buf3, "%s %s 温度:%s %s %s\n",
                cJSON_GetObjectItem(night, "sunDown")->valuestring,
                cJSON_GetObjectItem(night, "weather")->valuestring,
                cJSON_GetObjectItem(night, "temperature")->valuestring,
                cJSON_GetObjectItem(night, "wind")->valuestring,
                cJSON_GetObjectItem(night, "wind_pow")->valuestring);
        printf("%s", buf3);
    }

    // 解析当前天气信息
    cJSON *now = cJSON_GetObjectItem(data, "now");
    if (now != NULL) {
        sprintf(buf4, "当前:%s 温度:%s 湿度:%s %s%s 空气:%s\n",
                cJSON_GetObjectItem(now, "weather")->valuestring,
                cJSON_GetObjectItem(now, "temp")->valuestring,
                cJSON_GetObjectItem(now, "SD")->valuestring,
                cJSON_GetObjectItem(now, "WD")->valuestring,
                cJSON_GetObjectItem(now, "WS")->valuestring,
                cJSON_GetObjectItem(now, "aqi")->valuestring);
        printf("%s", buf4);
    }

    // 释放 cJSON 对象
    cJSON_Delete(root);

    // 释放动态分配的内存
    free(http_response);
    free(json);

}


int size(int lcd)
{
    struct fb_var_screeninfo vinfo; // 显卡设备的可变属性结构体
    ioctl(lcd, FBIOGET_VSCREENINFO, &vinfo); // 获取可变属性
    // 获得当前显卡所支持的虚拟区显存大小
    unsigned long width  = vinfo.xres;
    unsigned long height = vinfo.yres;
    unsigned long bpp    = vinfo.bits_per_pixel;
    unsigned long screen_size = width * height * bpp/8;
    return screen_size;
}

void showbitmap(bitmap *bm, char *p, int x, int y)
{    
    for(int j=0;j<bm->height;j++)
    {
        for(int i=0;i<bm->width;i++)
        {
            memcpy(p+(i+x)*4+4*800*(j+y),bm->map+i*4+4*bm->width*j,3);
        }
    }
}


int main(int argc, char const *argv[])
{
    if (argc != 3) {
        printf("Usage: %s <SERVER_IP> <PORT>\n", argv[0]);
        exit(1);
    }

    system("madplay start.mp3");
    // pthread_create(&t1, NULL, content1, NULL);
    api();

    pthread_t t1;

    pthread_create(&t1, NULL, ui, NULL);            //触控按键线程
    pthread_create(&t1, NULL, days, NULL);          //显示日期
    pthread_create(&t1, NULL, api_info_1, NULL);    //显示第一行的室外信息
    pthread_create(&t1, NULL, api_info_2, NULL);    //显示第二行的室外信息
    pthread_create(&t1, NULL, api_info_3, NULL);    //显示第三行的室外信息
    pthread_create(&t1, NULL, api_info_4, NULL);    //显示第四行的室外信息
    pthread_create(&t1, NULL, ecs_info, NULL);      //显示从华为云服务器得到的温湿度信息
    pthread_create(&t1, NULL, readts, NULL);        //触摸屏采集线程


    //接收温湿度数据线程
    // 创建套接字
    int sock = socket(AF_INET, SOCK_STREAM, 0);
    // 设置服务器地址
    struct sockaddr_in serv_addr;
    memset(&serv_addr, 0, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(atoi(argv[2]));
    inet_pton(AF_INET, argv[1], &serv_addr.sin_addr);
    // 连接服务器
    if (connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr))) {
        perror("connect failed");
        exit(1);
    }
    printf("Connected to server\n");
    // 接收数据循环
    while (1) {
        ssize_t len = read(sock, buffer, sizeof(buffer));
        if (len <= 0) break;
        printf("Received: %.*s", (int)len, buffer);
    }


    close(sock);
    pause();
    return 0;
}


char *http_request() {
    // 查天气（广州）
    char *requestHead = "GET /lundear/weather1d?areaCode=440114&areaCn=areaCn&ip=ip&lng=lng&lat=lat&needalarm=needalarm&need1hour=need1hour&need3hour=need3hour&needIndex=needIndex&needObserve=needObserve HTTP/1.1\r\n"
                        "Host: getweather.market.alicloudapi.com\r\n"
                        "Authorization: APPCODE a83865167d05434f92a60aa5edb9498d\r\n"
                        "Connection: close\r\n\r\n";

    return requestHead;
}
